#pragma once

#include "Common.h"
#include "ObjectPool.h"

//单例模式
class PageCache
{
public:
    static PageCache* GetInstance()
    {
        //懒汉模式
        return &_sInst;
    }

    // 获取从对象(某一个页号,这里给的是某一个pageid的地址)到span的映射
    Span* MapObjectToSpan(void* obj);

    // 释放空闲的span回到pagecache,合并相邻的span
    void ReleaseSpanToPageCache(Span* span);

    //获取一个k页的span
    //关于PageCache,例如当需要2页的span,如果此时2page桶没有挂span,就会往下找,如果找到了就切分成2页加n-2页
    //如何把2给中心缓存,n-2给对应的page桶(比如说n是10,那么切分后就放到第八个桶)
    Span* NewSpan(size_t k);
private:
    SpanList _spanLists[NPAGES]; // 总共128个桶
    ObjectPool<Span> _spanPool;
public:
    std::mutex _pageMtx; //用一个全局锁而不是桶锁

    // 关于用全局锁的具体原因：
    // 一开始的时候,PageCache是没有内容的,后续调用时,会向堆上申请8*128*1024个字节即1M
    // 之后再对这1M进行不断的切分给不同的page使用
    // 例如给2page,对应16KB,(默认一页8KB)那么就会在1M里面切出去16KB,之后把这个16KB给中心缓存,剩下的内容挂到126page上
    // 注意,这里的切分不是指真正的切分,只是把空间的使用权分配出去,因此每个page桶上挂的内存空间有很大概率是同一块1M的内存空间
    // 这种情况下使用桶锁就会有线程安全的问题了

    //仅作了解,不使用桶锁
    //使用桶锁也可以,每次page需要从大内存块里切分内容的时候,就进行加锁,而每个桶用的锁都是一样的
    //例如1page、2page、3page同时申请、4page释放要找小page合并
    //如果是1page先去进行切分,此时2page、3page、4page都把上下文给切走并进行等待
    //这种反而降低了效率

private:
    PageCache()
    {}
    PageCache(const PageCache&) = delete;
    static PageCache _sInst;
    std::unordered_map<PAGE_ID, Span*> _idSpanMap; //存放的是小内存块跟Span的映射关系

};